
import logging

from runtime.task import *

class AdminTask(Task):
    """
    This task is responsible for:
        * loading all 'sites' definitions from the datastore
        * associating an IP address and port number with each site.
        * starting HTTP tasks for each site.
    """

    def cb_start(self, method, uri, new_uri, result, context, **kwargs):
        """This function is called when the task starts. 
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), new_uri (%s)" % (uri, new_uri))
        site = Uri('services:type/http/site')
        self.configuration_rs = self.ds.find([(Uri('datastore:resource/type'), 
                                       'localhost:configuration')])[0]
        
        # 1. load all 'sites' definitions - there may be many!
        # 2. associate an IP address and port number with each site.
        # 3. send an 'add_site' message to HTTPTask for each site.
        #
        site_rs_list = self.ds.find([(Uri('datastore:resource/type'), 'package:type/site')])
        for site_rs in site_rs_list:
            self.logger.log(logging.INFO, "site (%s)" % site_rs.pretty_print())
            self.logger.log(logging.DEBUG, "site vars (%s)" % site_rs.get_vars())
            
            # Fields that should be set in the site definition from day one.
            #
            title = ""
            description = ""
            portoffset = 0
            default = ""
            templateuri = ""
            actions_list = []
            transforms_dict = {}
            is_new_port = False
            
            l = site_rs.get_metadata(Uri('dc:title'))
            if (len(l) > 0):
                title = l[0]
            l = site_rs.get_metadata(Uri('dc:description'))
            if (len(l) > 0):
                description = l[0]
            
            portoffset = site_rs.get_var(site.portoffset)
            default = site_rs.get_var(site.default)
            templateuri = site_rs.get_var(site.templateuri)
            actions_list = site_rs.get_var(site.actions)
            transforms_dict = site_rs.get_var(site.transforms)
            
            self.logger.log(logging.DEBUG, "site title (%s)" % title)
            self.logger.log(logging.DEBUG, "site description (%s)" % description)
            self.logger.log(logging.DEBUG, "site port offset (%s)" % portoffset)
            self.logger.log(logging.DEBUG, "site default (%s)" % default)
            self.logger.log(logging.DEBUG, "site template uri (%s)" % templateuri)
            self.logger.log(logging.DEBUG, "site actions list (%s)" % actions_list)
            self.logger.log(logging.DEBUG, "site templates dictionary (%s)" % transforms_dict)
            
            # Fields that should be set in the site definition the first time this
            # function is run.
            #
            port = 0
            
            try:
                port = site_rs.get_var(site.port)
            except KeyError:
                self.logger.log(logging.ERROR, "Non-existent 'port' in 'site' (%s) XML." % title)
                
            if (len(title) == 0):
                self.logger.log(logging.ERROR, "No 'site' title defined. Ignoring site.")
            else:
                # If port# has not yet been assigned to this site then this is
                # the first call (ever) of cb_start(). So, let's assign the port#
                # and write back to the datastore after processing all sites in
                # the current site resource.
                #
                # Note: ip-address is always taken from the configuration resource,
                #       not the site resource.
                #
                if (port == 0):
                    self.logger.log(logging.DEBUG, "************ No port number found in site (%s) definition. Assigning..." % title)
                    
                    # manufacture port number for site...
                    #
                    port = self.configuration_rs.nodeportrangelow + portoffset
                    site_rs.set_var(site.port, port)
                    is_new_port = True

                # Create and populate the Resource to send to HTTPTask to start 
                # a listener for this IP-address/port# combo.
                #
                rs = Resource()
                rs.set_metadata(Uri('dc:title'), title)
                rs.set_metadata(Uri('dc:description'), description)
                rs.set_var(site.ipaddress, self.configuration_rs.nodeipaddress)
                rs.set_var(site.port, port)
                rs.set_var(site.default, default)
                rs.set_var(site.templateuri, templateuri)
                rs.set_var(site.actions, actions_list)
                rs.set_var(site.transforms, transforms_dict)

                # destination of message - HTTPTask
                # - 'listener' is appended to HTTPTask Uri because HTTPTask 
                #   is waiting on 'task:base/service/httptask/*' but won't see
                #   messages to 'task:base/service/httptask'.
                #
                http_uri = self.spaces_context.uri('task:base/service/httptask')
                
                # send 'add_site' message to HTTPTask (Any 'put' message 
                # to HTTPTask is interpreted as a site addition.                         
                #
                self.spaces_context.put(http_uri.add("listen"), rs)
            
                # write assigned port#s to datastore.
                #
                if (is_new_port is True):
                    self.ds.put(site_rs)
            
    def cb_stop(self, method, uri, res, result, context, **kwargs):
        """This function will stop any listeners still running.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param res: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "method (%s), uri (%s)" % (method, uri))